#ifndef __COMMON_H__
#define __COMMON_H__

#include <Windows.h>
#include <winsock.h>

/*
 * values of the protocol headers
 */
#define PROTOCOL_PREFIX 0xF00D
#define CLIENT_HELLO_MSG_ID 0x01
#define SERVER_HELLO_MSG_ID 0x02
#define DIR_CONTENT_REQ_MSG_ID 0x01
#define DIR_CONTENT_RES_MSG_ID 0x02
#define FILE_CONTENT_REQ_MSG_ID 0x03
#define FILE_CONTENT_RES_MSG_ID 0x04

/*
 * macros for mempry access
 */
#define MEM_TO_PTR(ptr,type) ((type*)(ptr))
#define MEM_TO_VAL(ptr,type) (*MEM_TO_PTR(ptr,type))

/*
 * timeout when waiting for packet
 */
#define TIMEOUT_SECONDS 1

/* 
 * protocol ports
 */
#define SERVER_UDP_PORT 1337
#define CLIENT_UDP_PORT 1338

/*
 * messages header's lengh
 */
#define CLIENT_HELLO_MSG_LEN 7
#define SERVER_HELLO_MSG_LEN 13

#define FILE_REQ_HEADER_SIZE 5
#define FILE_RES_HEADER_SIZE 7

#define FILELIST_REQ_HEADER_SIZE 5
#define FILELIST_RES_HEADER_SIZE 5

#define FILELIST_ITEM_HEADER_SIZE 7

/*
 * block size when transfering file
 */
#define FILE_BLOCK_SIZE 1024

/*
 * value of suid encryption keys
 */
#define SERVER_UID_KEY 0xFEEDBEBE
#define SERVER_TCP_PORT_KEY 0x0980

typedef struct sockaddr sockaddr;

/*
 * this method creates TCP/UDP socket and bind it to port
 */
SOCKET createSocket(int type,unsigned short port,BOOL enableBrodcast)
{
	SOCKET sd;
    char nOptiontValue = 1;
    
	struct sockaddr_in s_in;
	

	sd = socket(AF_INET, type, 0);
	
	if (sd == INVALID_SOCKET)
	{
	  printf("Error: Could not create socket.\n");
	  return INVALID_SOCKET;
	}

	do
	{
		if (enableBrodcast)
		{
			// enable UDP brodcast
			if (setsockopt(sd,SOL_SOCKET, SO_BROADCAST,&nOptiontValue,sizeof(nOptiontValue)))
			{
			   printf("Error: Could not set socket option.\n");
 			   break;
			}
		}	

		memset((void *)&s_in, '\0', sizeof(struct sockaddr_in));

		//Set family and port
		s_in.sin_family = AF_INET;
		s_in.sin_port = htons(port);

		if (bind(sd, (struct sockaddr *)&s_in, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
		{
		   printf("Error: Cannot bind port to socket.\n");
		   break;
		}

		return sd;
#pragma warning(suppress:4127) //enable single exit point
	}while(FALSE);

	closesocket(sd);

	return INVALID_SOCKET;


}

/*
 * this method create a server TCP socket
 */
SOCKET createServerTCPSocket(unsigned short portNumber)
{
	SOCKET sd = createSocket(SOCK_STREAM,portNumber,FALSE);
	struct sockaddr_in s_in;

	if (sd == SOCKET_ERROR)
	{
		return sd;
	}

	//listen to the port for incomming connections
	do
	{
		memset((void *)&s_in, '\0', sizeof(struct sockaddr_in));

		//Set family and port
		s_in.sin_family = AF_INET;
		s_in.sin_port = htons(portNumber);

		if (listen(sd,SOMAXCONN) == SOCKET_ERROR)
		{
			break;
		}

		return sd;
#pragma warning(suppress:4127) //enable single exit point
	}while(FALSE);
	
	closesocket(sd);

	return (SOCKET)NULL;
}

/*
 * this method creates UDP socket
 */
SOCKET createUDPSocket(unsigned short portNumber,BOOL enableBrodcast)
{
	return createSocket(SOCK_DGRAM,portNumber,enableBrodcast);
}


/*
 * this method recv a given buffer through TCP connection
 */
int recvTcpBuffer(int sd,char* buffer,size_t size)
{
	fd_set readfds; 
	size_t readCount = 0;
    size_t localRead = 0;
	struct timeval timeout;
	int selectRes;

	//wait 5 seconds for server response
	timeout.tv_sec = TIMEOUT_SECONDS;
	timeout.tv_usec = 0;
    
	//while there is more data to read
	while(readCount != size)
	{

		FD_ZERO(&readfds);
#pragma warning(suppress:4127)
		FD_SET(sd,&readfds);
		//wait for incomming communication		   
		selectRes = select(sd+1,&readfds,NULL,NULL,&timeout);
	   
		if (selectRes == 0 || FD_ISSET(sd,&readfds)==0)
		{
			return SOCKET_ERROR;
		}

		localRead = recv(sd,buffer+readCount,size-readCount,0);

		if (localRead == SOCKET_ERROR)
		{
			return SOCKET_ERROR;
		}
		readCount+=localRead;
	}

	return readCount;

}

/*
 * this method sends buffer through TCP connection
 */
int sendTcpBuffer(int sd, char* buffer, size_t size){

        size_t sentCount = 0;
        size_t localSent = 0;

		//while there is more data to send
        while (sentCount != size) {
                localSent = send(sd, buffer+sentCount , size-sentCount, 0);
                if (localSent == SOCKET_ERROR) {
                        return SOCKET_ERROR;
                }
                else {
                        sentCount += localSent;
                }
        }

        
        return size;
}

#endif